﻿#ifndef VIDEOCTL_H
#define VIDEOCTL_H

#include <QObject>
#include <QThread>
#include <QString>
#include <QImage>

#ifdef _windows_
#include <windows.h>
#include <objbase.h>
#endif

#ifdef linux
#include <unistd.h>
#endif

#include <thread>

#include <inttypes.h>
#include <math.h>
#include <limits.h>
#include <signal.h>
#include <stdint.h>
#include <assert.h>
#include <memory>
extern "C"{
#include "libavutil/avstring.h"
#include "libavutil/channel_layout.h"
#include "libavutil/eval.h"
#include "libavutil/mathematics.h"
#include "libavutil/pixdesc.h"
#include "libavutil/imgutils.h"
#include "libavutil/dict.h"
#include "libavutil/fifo.h"
#include "libavutil/parseutils.h"
#include "libavutil/samplefmt.h"
#include "libavutil/time.h"
#include "libavutil/bprint.h"
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libavfilter/avfilter.h"
#include "libavdevice/avdevice.h"
#include "libswscale/swscale.h"
#include "libavutil/opt.h"
#include "libavcodec/avfft.h"
#include "libswresample/swresample.h"

#include "SDL2/SDL.h"
}

#define MAX_QUEUE_SIZE (15 * 1024 * 1024)
#define MIN_FRAMES 25
#define EXTERNAL_CLOCK_MIN_FRAMES 2
#define EXTERNAL_CLOCK_MAX_FRAMES 10

/* Minimum SDL audio buffer size, in samples. */
#define SDL_AUDIO_MIN_BUFFER_SIZE 512
/* Calculate actual buffer size keeping in mind not cause too frequent audio callbacks */
#define SDL_AUDIO_MAX_CALLBACKS_PER_SEC 30

/* Step size for volume control in dB */
#define SDL_VOLUME_STEP (0.75)

/* no AV sync correction is done if below the minimum AV sync threshold */
#define AV_SYNC_THRESHOLD_MIN 0.04
/* AV sync correction is done if above the maximum AV sync threshold */
#define AV_SYNC_THRESHOLD_MAX 0.1
/* If a frame duration is longer than this, it will not be duplicated to compensate AV sync */
#define AV_SYNC_FRAMEDUP_THRESHOLD 0.1
/* no AV correction is done if too big error */
#define AV_NOSYNC_THRESHOLD 10.0

/* maximum audio speed change to get correct sync */
#define SAMPLE_CORRECTION_PERCENT_MAX 10

/* external clock speed adjustment constants for realtime sources based on buffer fullness */
#define EXTERNAL_CLOCK_SPEED_MIN  0.900
#define EXTERNAL_CLOCK_SPEED_MAX  1.010
#define EXTERNAL_CLOCK_SPEED_STEP 0.001

/* we use about AUDIO_DIFF_AVG_NB A-V differences to make the average */
#define AUDIO_DIFF_AVG_NB   20

/* polls for possible required screen refresh at least this often, should be less than 1/fps */
#define REFRESH_RATE 0.01

/* NOTE: the size must be big enough to compensate the hardware audio buffersize size */
/* TODO: We assume that a decoded and resampled frame fits into this buffer */
#define SAMPLE_ARRAY_SIZE (8 * 65536)

#define CURSOR_HIDE_DELAY 1000000

#define USE_ONEPASS_SUBTITLE_RENDER 1

typedef struct _VideoInfo{

    QString codec_name;
    int width;
    int height;
    float framerate;
    QString pix_format;
    int bit_rate;

    _VideoInfo()
    {
        codec_name = "";
    }

}VideoInfo;

typedef struct _AudioInfo{

    QString codec_name;
    int sample_rate;
    int channel;
    int sample_bits;
    int bit_rate;
    _AudioInfo()
    {
        codec_name = "";
    }

}AudioInfo;

typedef struct _MediaInfo{

    VideoInfo vinfo;
    AudioInfo ainfo;

}MediaInfo;

//数据包列表
typedef struct MyAVPacketList {
    AVPacket* pkt;
    int serial;
} MyAVPacketList;

//数据包队列
typedef struct PacketQueue {
    AVFifo* pkt_list;
    int nb_packets;
    int size;
    int64_t duration;
    int abort_request;
    int serial;
    SDL_mutex* mutex;
    SDL_cond* cond;
} PacketQueue;

#define VIDEO_PICTURE_QUEUE_SIZE 3
#define SUBPICTURE_QUEUE_SIZE 16
#define SAMPLE_QUEUE_SIZE 9
#define FRAME_QUEUE_SIZE FFMAX(SAMPLE_QUEUE_SIZE, FFMAX(VIDEO_PICTURE_QUEUE_SIZE, SUBPICTURE_QUEUE_SIZE))

void sdl_audio_callback(void *opaque, Uint8 *stream, int len);

//音频参数
typedef struct AudioParams {
    int freq;
    AVChannelLayout ch_layout;
    enum AVSampleFormat fmt;
    int frame_size;
    int bytes_per_sec;
} AudioParams;

//时钟
typedef struct Clock {
    double pts;           /* clock base */
    double pts_drift;     /* clock base minus time at which we updated the clock */
    double last_updated;
    double speed;
    int serial;           /* clock is based on a packet with this serial */
    int paused;
    int *queue_serial;    /* pointer to the current packet queue serial, used for obsolete clock detection */
} Clock;

/* Common struct for handling all types of decoded data and allocated render buffers. */
//解码后的帧
typedef struct Frame {
    AVFrame* frame;
    AVSubtitle sub;
    int serial;
    double pts;           /* presentation timestamp for the frame */
    double duration;      /* estimated duration of the frame */
    int64_t pos;          /* byte position of the frame in the input file */
    int width;
    int height;
    int format;
    AVRational sar;
    int uploaded;
    int flip_v;
    AVPacket* pkt;
} Frame;

//帧队列
typedef struct FrameQueue {
    Frame queue[FRAME_QUEUE_SIZE];
    int rindex;
    int windex;
    int size;
    int max_size;
    int keep_last;
    int rindex_shown;
    SDL_mutex* mutex;
    SDL_cond* cond;
    PacketQueue* pktq;
} FrameQueue;

enum {
    AV_SYNC_AUDIO_MASTER, /* default choice */
    AV_SYNC_VIDEO_MASTER,
    AV_SYNC_EXTERNAL_CLOCK, /* synchronize to an external clock */
};

//解码器，管理数据队列
typedef struct Decoder {
    AVPacket* pkt;
    PacketQueue* queue;
    AVCodecContext* avctx;
    int pkt_serial;
    int finished;
    int packet_pending;
    SDL_cond* empty_queue_cond;
    int64_t start_pts;
    AVRational start_pts_tb;
    int64_t next_pts;
    AVRational next_pts_tb;
    std::thread decode_thread;
} Decoder;

//视频状态，管理所有的视频信息及数据
typedef struct PlayerCore {
    std::thread read_tid; //读取线程
    AVInputFormat *iformat;
    int abort_request; //停止读取标志
    int force_refresh;
    int paused;
    int last_paused;
    int queue_attachments_req;
    int seek_req = 0;
    int seek_flags;
    int64_t seek_pos;
    int64_t seek_rel;
    int read_pause_return;
    AVFormatContext *ic;
    AVFormatContext *out_ctx;
    AVCodecContext* video_encoder = NULL;
    AVCodecContext* audio_encoder = NULL;
    int out_v_index;
    int out_a_index;
    int realtime;
    Clock audclk;
    Clock vidclk;
    Clock extclk;

    FrameQueue pictq;
    FrameQueue sampq;

    Decoder auddec;
    Decoder viddec;

    int audio_stream;

    int av_sync_type;

    double audio_clock;
    int audio_clock_serial;
    double audio_diff_cum; /* used for AV difference average computation */
    double audio_diff_avg_coef;
    double audio_diff_threshold;
    int audio_diff_avg_count;
    AVStream *audio_st = NULL;
    PacketQueue audioq;
    int audio_hw_buf_size;
    uint8_t *audio_buf;
    uint8_t *audio_buf1;
    unsigned int audio_buf_size; /* in bytes */
    unsigned int audio_buf1_size;
    int audio_buf_index; /* in bytes */
    int audio_write_buf_size;
    int audio_volume;

    struct AudioParams audio_src;

    struct AudioParams audio_tgt;
    struct SwrContext *swr_ctx;
    int frame_drops_early;
    int frame_drops_late;

    int16_t sample_array[SAMPLE_ARRAY_SIZE];
    int sample_array_index;
    int last_i_start;
    RDFTContext *rdft;
    int rdft_bits;
    FFTSample *rdft_data;
    int xpos;
    double last_vis_time;

    double frame_timer;  // 上一次显示图片的时间
    double frame_last_returned_time;
    double frame_last_filter_delay;
    int video_stream;
    AVStream *video_st = NULL;
    PacketQueue videoq;
    double max_frame_duration;      // maximum duration of a frame - above this, we consider the jump a timestamp discontinuity

    struct SwsContext *rgb_convert_ctx = NULL;
    unsigned char* out_buffer_video = NULL;
    AVFrame* frame_rgb = NULL;
    int eof;

    std::string filename;
    int width, height, xleft, ytop;
    int step;

    int last_video_stream, last_audio_stream;

    SDL_cond *continue_read_thread;

    float play_speed = 1;

    float audio_pts_gap = 0.00; // 音频pts间隔
    long out_audio_pts;         // 当前音频帧的pts

    float video_pts_gap = 0.00; // 视频pts间隔
    long out_video_pts;         // 当前视频帧的pts

} PlayerCore;

class Qffplayer : public QObject
{
    Q_OBJECT

public:
    explicit Qffplayer( QObject *parent = nullptr);
    ~Qffplayer();
    bool StartPlay(QString strFileName);
    void stopPlay();
    std::shared_ptr<MediaInfo> get_mediainfo();

    void set_facedetect(bool isDetect);
    bool get_facedetect();
    int transcode2h264(QImage img);
    bool need_open_output = false;  // 是否存本地录像
    int set_play_speed(float s)
    {
        if(m_CurStream == NULL)
            return -1;

        if(s == m_CurStream->play_speed)
            return 0;
        if(m_CurStream->realtime == 1)
            return -1;

        m_CurStream->play_speed = s;
        SDL_CloseAudio();
        SDL_CloseAudioDevice(this->audio_dev);
        audio_open(this, &m_CurStream->audio_st->codecpar->ch_layout,  m_CurStream->audio_st->codecpar->sample_rate*s, &m_CurStream->audio_tgt);
        SDL_PauseAudioDevice(audio_dev, 0);

        return 0;
    }

signals:
    /* type == 0 播放成功
     * type == 1 播放结束
     * type ==-1 播放失败， data：失败原因
     * type == 2 获取文件长度，宽高
     * type == 3 更新文件播放时间
     * */
    void sigPlayerCb(int type, QString data,int w, int h);
    void sigDisplay(QImage img, long pts);

public:
    int setPlaySeek(int time);
    void setPlayVolume(double dPercent);
    void OnSeekForward();
    void OnSeekBack();
    void OnAddVolume();
    void OnSubVolume();
    int setPause();
    int step_to_next_frame();

private:
    bool faceDetect = false;  // 是否启用人脸检测
    float export_pts = 0;
    bool Init();
    int audio_decode_frame(PlayerCore *is);
    void update_sample_display(PlayerCore *is, short *samples, int samples_size);
    void set_clock_at(Clock *c, double pts, int serial, double time);
    void sync_clock_to_slave(Clock *c, Clock *slave);
    int open_encoder();
    void close_output();
    int open_output();
    int get_video_frame(PlayerCore *is, AVFrame *frame);
    int audio_thread(void *arg);
    int video_thread(void *arg);
    int synchronize_audio(PlayerCore *is, int nb_samples);
    int audio_open(void* opaque, AVChannelLayout* wanted_channel_layout, int wanted_sample_rate, struct AudioParams* audio_hw_params);
    int stream_component_open(PlayerCore *is, int stream_index);
    int stream_has_enough_packets(AVStream *st, int stream_id, PacketQueue *queue);
    int is_realtime(AVFormatContext *s);
    void ReadThread(PlayerCore *CurStream);
    void LoopThread(PlayerCore *CurStream);
    PlayerCore *stream_open(const char *filename);
    void stream_cycle_channel(PlayerCore *is, int codec_type);
    void refresh_loop_wait_event(PlayerCore *is);
    void seek_chapter(PlayerCore *is, int incr);
    void video_refresh(void *opaque, double *remaining_time);
    int queue_picture(PlayerCore *is, AVFrame *src_frame, double pts, double duration, int64_t pos, int serial);
    //更新音量
    void UpdateVolume(int sign, double step);
    void do_exit(PlayerCore* &is);
    void video_image_display(PlayerCore *is);
    void stream_component_close(PlayerCore *is, int stream_index);
    void stream_close(PlayerCore *is);
    double get_clock(Clock *c);

    void set_clock(Clock *c, double pts, int serial);
    void set_clock_speed(Clock *c, double speed);
    void init_clock(Clock *c, int *queue_serial);
    
    int get_master_sync_type(PlayerCore *is);
    double get_master_clock(PlayerCore *is);
    void check_external_clock_speed(PlayerCore *is);
    void stream_seek(PlayerCore *is, int64_t pos, int64_t rel);
    void stream_toggle_pause(PlayerCore *is);
    void toggle_pause(PlayerCore *is);
    void step_to_next_frame(PlayerCore *is);
    double compute_target_delay(double delay, PlayerCore *is);
    double vp_duration(PlayerCore *is, Frame *vp, Frame *nextvp);
    void update_video_pts(PlayerCore *is, double pts, int64_t pos, int serial);
    int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub, AVPacket** audio_pkt = NULL);
private:
    long open_out_pts = -1;
    long prevPts;  // 上一次通知UI时的pts
    int startup_volume;
    std::thread m_tPlayLoopThread;
    PlayerCore* m_CurStream;
    bool m_bPlayLoop; //刷新循环标志
    SDL_AudioDeviceID audio_dev;
    bool audio_playing = false;
    friend    void sdl_audio_callback(void *opaque, Uint8 *stream, int len);
};

#endif // VIDEOCTL_H
